package com.example.opengldemo.util;

import android.opengl.GLES11Ext;
import android.opengl.GLES30;
import android.util.Log;

import java.nio.FloatBuffer;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

public class ShaderHandle {
    public static final String APOSITION = "aPosition";
    public static final String ATEXCOORS = "aTexCoors";
    public static final String ANORMAL = "aNormal";
    public static final String ACOLOR = "aColor";

    public static final String UMVPMATRIX = "uMVPMatrix";
    public static final String UMMATRIX = "uMMatrix";
    public static final String UTMatrix = "uTMatrix";

    public static final String ULIGHT = "uLightLocation";
    public static final String UCAMERA = "uCamera";

    public static final String UTEXTURE = "sTexture";

    private int m_U_TMatrixHandle = -1;
    private String m_U_TMatrixHandleName = UTMatrix;

    private int m_A_PositionHandle = -1;
    private String m_A_PositionHandleName =APOSITION;

    private int m_A_TexCoorsHandle = -1;
    private String m_A_TexCoorsHandleName =  ATEXCOORS;

    private int m_A_NormalHandle = -1;
    private String m_A_NormalHandleName  = ANORMAL;

    private int m_A_ColorHandle = -1;
    private String m_A_ColorHandleName = ACOLOR;

    private int m_U_MVPMatrixHandle = -1;
    private String m_U_MVPMatrixHandleName =UMVPMATRIX;

    private int m_U_MMatrixHandle = -1;
    private String m_U_MMatrixHandleName = UMMATRIX;

    private int m_U_LightHandle = -1;
    private String m_U_LightHandleName =ULIGHT;

    private int m_U_CameraHandle = -1;
    private String m_U_CameraHandleName = UCAMERA;

    private int m_U_textureHandle = -1;
    private String m_U_textureHandleName = UTEXTURE;

    private int mProgram ;

    public int[] UHandleArray = new int[20];
    public int[] AHandleArray = new int[10];

    private Map<String , Integer> handleMaps ;


    public ShaderHandle(int program ){
        this(program , true);
    }

    public ShaderHandle(int program  , boolean isInit){
        handleMaps  = new HashMap<>() ;
        mProgram = program  ;
        if(isInit){
            build();
        }
    }

    private void handleMaps(String handleName , Integer index){
        // index == - 2  Attrib
        // index == - 3  Uniform
        // index == - 1  default
        handleMaps.put(handleName , index) ;
    }


    public int getAHandle(String name){
        return GLES30.glGetAttribLocation(mProgram , name);
    }
    public int getUHandle(String name){
        return  GLES30.glGetUniformLocation( mProgram, name);
    }

    public int getProgram() {
        return mProgram;
    }

    public ShaderHandle setM_A_PositionHandleName(String A_PositionHandleName) {
        this.m_A_PositionHandleName = A_PositionHandleName;
        return this ;
    }

    public ShaderHandle setM_A_TexCoorsHandleName(String A_TexCoorsHandleName) {
        this.m_A_TexCoorsHandleName = A_TexCoorsHandleName;
        return this ;
    }

    public ShaderHandle setM_A_NormalHandleName(String A_NormalHandleName) {
        this.m_A_NormalHandleName = A_NormalHandleName;
        return this ;
    }

    public ShaderHandle setM_A_ColorHandleName(String A_ColorHandleName) {
        this.m_A_ColorHandleName = A_ColorHandleName;
        return this ;
    }

    public ShaderHandle setM_U_MVPMatrixHandleName(String U_MVPMatrixHandleName) {
        this.m_U_MVPMatrixHandleName = U_MVPMatrixHandleName;
        return this ;

    }

    public ShaderHandle setM_U_MMatrixhandleName(String U_MMatrixhandleName) {
        this.m_U_MMatrixHandleName = U_MMatrixhandleName;
        return this ;
    }

    public ShaderHandle setM_U_TMatrixhandleName(String U_TMatrixhandleName) {
        this.m_U_TMatrixHandleName = U_TMatrixhandleName;
        return this ;
    }


    public ShaderHandle setM_U_LightHandleName(String U_LightHandleName) {
        this.m_U_LightHandleName = U_LightHandleName;
        return this ;
    }

    public ShaderHandle setM_U_CameraHandleName(String U_CameraHandleName) {
        this.m_U_CameraHandleName = U_CameraHandleName;
        return this ;
    }

    public ShaderHandle setM_U_textureHandle(int U_textureHandle) {
        this.m_U_textureHandle = U_textureHandle;
        return this ;
    }

    public ShaderHandle build(){

        this.m_U_MVPMatrixHandle= GLES30.glGetUniformLocation(mProgram , m_U_MVPMatrixHandleName) ;
        this.m_U_MMatrixHandle=   GLES30.glGetUniformLocation(mProgram , m_U_MMatrixHandleName) ;
        this.m_U_TMatrixHandle=   GLES30.glGetUniformLocation(mProgram , m_U_TMatrixHandleName) ;
        this.m_U_LightHandle =    GLES30.glGetUniformLocation(mProgram , m_U_LightHandleName) ;
        this.m_U_CameraHandle =   GLES30.glGetUniformLocation(mProgram , m_U_CameraHandleName) ;
        this.m_A_PositionHandle = GLES30.glGetAttribLocation(mProgram , m_A_PositionHandleName);
        this.m_A_TexCoorsHandle = GLES30.glGetAttribLocation(mProgram , m_A_TexCoorsHandleName);
        this.m_A_NormalHandle =   GLES30.glGetAttribLocation(mProgram , m_A_NormalHandleName);
        this.m_A_ColorHandle =    GLES30.glGetAttribLocation(mProgram , m_A_ColorHandleName);
        this.m_U_textureHandle = GLES30.glGetUniformLocation(mProgram , m_U_textureHandleName);

        if(handleMaps != null && handleMaps.size()>0){
            Set<Map.Entry<String, Integer>> entries = handleMaps.entrySet();
            Iterator<Map.Entry<String, Integer>> iterator = entries.iterator();
            while (iterator.hasNext()){
                Map.Entry<String, Integer> entry = iterator.next();

                Integer handletype  =  entry.getValue();
                String handleName = entry.getKey();

                if(handletype == null || handleName == null) continue;

                if( handletype == -2){
                    int handle  =    GLES30.glGetAttribLocation(mProgram , handleName);
                    handleMaps.put(handleName , handle) ;
                }else if( handletype == -3){
                    int handle  =    GLES30.glGetUniformLocation(mProgram , handleName);
                    handleMaps.put(handleName , handle) ;
                }else{
                    handleMaps.put(handleName , -1) ;
                }
            }
        }

        return this ;
    }

    public ShaderHandle program(){
        GLES30.glUseProgram(getProgram());
        return this ;
    }

    public ShaderHandle bindVertexHandle(FloatBuffer floatBuffer){
        if(!checkEffect(get_A_PositionHandle())) return this ;

        GLES30.glVertexAttribPointer(
                get_A_PositionHandle() ,
                3,
                GLES30.GL_FLOAT ,
                false ,
                3*4 ,
                floatBuffer
        );
        return this ;
    }
    public ShaderHandle bindTexCoorsHandle(FloatBuffer floatBuffer){
        if(!checkEffect(get_A_TexCoorsHandle())) return this ;

        GLES30.glVertexAttribPointer(
                get_A_TexCoorsHandle() ,
                2,
                GLES30.GL_FLOAT ,
                false ,
                2*4 ,
                floatBuffer
        );
        return this ;
    }
    public ShaderHandle bindNormalHandle(FloatBuffer floatBuffer){
        if(!checkEffect(get_A_NormalHandle())) return this ;

        GLES30.glVertexAttribPointer(
                get_A_NormalHandle() ,
                3,
                GLES30.GL_FLOAT ,
                false ,
                3*4 ,
                floatBuffer
        );
        return this ;
    }
    public ShaderHandle bindColorsHandle(FloatBuffer floatBuffer){
        if(!checkEffect(get_A_ColorHandle())) return this ;

        GLES30.glVertexAttribPointer(
                get_A_ColorHandle() ,
                4,
                GLES30.GL_FLOAT ,
                false ,
                4*4 ,
                floatBuffer
        );
        return this ;
    }

    public ShaderHandle bindMVPMaxtrixHandle(){
        if(!checkEffect(get_U_MVPMatrixHandle())) return this ;
        GLES30.glUniformMatrix4fv(get_U_MVPMatrixHandle() , 1 , false , MatrixState.getFinalMatrix()  ,0);
        return this ;
    }
    public ShaderHandle bindMVPMaxtrixHandle(float[] floats){
        if(!checkEffect(get_U_MVPMatrixHandle())) return this ;
        GLES30.glUniformMatrix4fv(get_U_MVPMatrixHandle() , 1 , false , floats ,0);
        return this ;
    }

    public ShaderHandle bindMMaxtrixHandle(){
        if(!checkEffect(get_U_MMatrixhandle())) return this ;
        GLES30.glUniformMatrix4fv(get_U_MMatrixhandle() , 1 , false , MatrixState.getMMatrix()  ,0);
        return this ;
    }
    public ShaderHandle bindMMaxtrixHandle(float[] floats){
        if(!checkEffect(get_U_MMatrixhandle())) return this ;
        GLES30.glUniformMatrix4fv(get_U_MMatrixhandle() , 1 , false , floats  ,0);
        return this ;
    }

    public ShaderHandle bindTMaxtrixHandle(){
        if(!checkEffect(getM_U_TMatrixHandle())) return this ;
        GLES30.glUniformMatrix4fv(getM_U_TMatrixHandle() , 1 , false , MatrixUtils.getOriginalMatrix()  ,0);
        return this ;
    }
    public ShaderHandle bindTMaxtrixHandle(float[] floats){
        if(!checkEffect(getM_U_TMatrixHandle())) return this ;
        GLES30.glUniformMatrix4fv(getM_U_TMatrixHandle() , 1 , false , floats  ,0);
        return this ;
    }

    public ShaderHandle bindCameraHandle(){
        if(!checkEffect(get_U_CameraHandle())) return this ;
        GLES30.glUniform3fv(get_U_CameraHandle() , 1 , MatrixState.cameraLocationFB );
       return this ;
    }
    public ShaderHandle bindLightHandle(){
        if(!checkEffect(get_U_LightLHandle())) return this ;
        GLES30.glUniform3fv(get_U_LightLHandle() , 1 , MatrixState.lightLocationFB );
        return this ;
    }

    public ShaderHandle bindDefaultTextureHandle(int id){
        if(!checkEffect(get_U_textureHandle())) return this ;
        GLES30.glActiveTexture(GLES30.GL_TEXTURE0);
        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D , id);
        GLES30.glUniform1i(get_U_textureHandle(),0);
        return this ;
    }

    public ShaderHandle bindDefault3DTextureHandle(int id){
        if(!checkEffect(get_U_textureHandle())) return this ;
        //GLES30.glActiveTexture(GLES30.GL_TEXTURE_CUBE_MAP);
        GLES30.glEnable(GLES30.GL_TEXTURE_CUBE_MAP);
        GLES30.glBindTexture(GLES30.GL_TEXTURE_CUBE_MAP , id);
        GLES30.glUniform1i(get_U_textureHandle(),0);
        return this ;
    }

    public ShaderHandle bindDefaultOesTextureHandle(int id){
        if(!checkEffect(get_U_textureHandle())) return this ;
        GLES30.glActiveTexture(GLES30.GL_TEXTURE0);
        GLES30.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,id);
        GLES30.glUniform1i(get_U_textureHandle(),0);
        return this ;
    }

    public void run(int mode , int vCount){
        if(checkEffect(get_A_NormalHandle()))GLES30.glEnableVertexAttribArray(get_A_NormalHandle());
        if(checkEffect(get_A_PositionHandle()))GLES30.glEnableVertexAttribArray(get_A_PositionHandle());
        if(checkEffect(get_A_TexCoorsHandle()))GLES30.glEnableVertexAttribArray(get_A_TexCoorsHandle());
        if(checkEffect(get_A_ColorHandle()))GLES30.glEnableVertexAttribArray(get_A_ColorHandle());

        GLES30.glDrawArrays(mode , 0 , vCount);

        if(checkEffect(get_A_NormalHandle()))GLES30.glDisableVertexAttribArray(get_A_NormalHandle());
        if(checkEffect(get_A_PositionHandle()))GLES30.glDisableVertexAttribArray(get_A_PositionHandle());
        if(checkEffect(get_A_TexCoorsHandle()))GLES30.glDisableVertexAttribArray(get_A_TexCoorsHandle());
        if(checkEffect(get_A_ColorHandle()))GLES30.glDisableVertexAttribArray(get_A_ColorHandle());

    }

    public void destroy() {
        GLES30.glDeleteProgram(mProgram);
    }

    private boolean checkEffect(int value){
        return value != -1 ;
    }

    public int get_A_PositionHandle() {
        return m_A_PositionHandle;
    }

    public int get_A_TexCoorsHandle() {
        return m_A_TexCoorsHandle;
    }

    public int get_A_NormalHandle() {
        return m_A_NormalHandle;
    }

    public int get_A_ColorHandle() {
        return m_A_ColorHandle;
    }

    public int get_U_MVPMatrixHandle() {
        return m_U_MVPMatrixHandle;
    }

    public int get_U_MMatrixhandle() {
        return m_U_MMatrixHandle;
    }

    public int get_U_LightLHandle() {
        return m_U_LightHandle;
    }

    public int get_U_CameraHandle() {
        return m_U_CameraHandle;
    }

    public int get_U_textureHandle() {
        return m_U_textureHandle;
    }

    public int getM_U_TMatrixHandle(){ return m_U_TMatrixHandle ; }

}
